package cn.gok.servlet;

import java.io.*;

/**
 * @Author: chenfan
 * @Date: 2020/12/7 23:46
 * @Description:
 *      因为部署的项目不处于当前源码的类路径下，
 *      所以我们自定义一个类加载器 去加载 部署的项目中的类
 */
public class WebClassLoader extends ClassLoader {

    /**
     * 指定项目的基础路径， 然后去加载该路径下的所有类
     */
    private String webAppPath;

    public WebClassLoader(String webAppPath) {
        this.webAppPath = webAppPath;
    }

    /**
     * 复写findClass方法
     * 因为自定义的类加载器WebClassLoader继承了ClassLoader类，继承了其所有方法
     * 通过loadClass(String name)方法加载类的时候：
     *  1、会检查该类是否已经加载，如果没有，会依次委托WebClassLoader的父类、父类的父类
     *    等类加载器依次尝试加载，都失败则执行findClass(String name)试图获取待加载的类的Class对象
     *  2、而ClassLoader中findClass(String name)方法直接抛出ClassNotFoundException(name)
     *  3、因此复写findClass(String name)方法就是以自定义的方式获取类（在父类都无法加载的情况）
     *  4、通过将某个.class文件读入一个byte类型的数组中，然后通过defineClass方法
     *    获取该字节码文件（Class对象）并返回
     *  5、因此，当1中的情况出现时，本类即可通过findClass(String name)获取Class对象给
     *    loadClass(String name)方法，使其返回同一个Class对象
     *
     *    因为protected权限修饰符，其他类调用本类加载某个类只能用loadClass方法
     *
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        /*
            先把所有全类名中所有的点 替换成 /
                    比如： cn.gok.test.TestServlet  变成 cn/gok/test/TestServlet
            接着在拼接上.class 后缀
            再和项目路径进行拼接
         */
        String classPath = webAppPath +"/" + name.replace('.', '/').concat(".class");

        //定义Class对象引用
        Class<?> clazz = null;
        InputStream is =null;

        try {
            is = new FileInputStream(classPath);
            byte[] bytes = new byte[is.available()];
            is.read(bytes);
            //将该字节数组变成字节码文件
            clazz = defineClass(null, bytes, 0, bytes.length);

        } catch (IOException e) {
            e.printStackTrace();
            //关闭流
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }

        return clazz;
    }
}
